"use strict";

const ApiGateway = require("moleculer-web");
const multer = require('multer');
const fs = require('fs');
const path = require('path');
const uploadConfig = require('../config/upload');
const jwt = require('jsonwebtoken');

// 配置 multer 存储
const storage = multer.diskStorage({
	destination: function (req, file, cb) {
		// 使用配置的安全路径，不接受客户端自定义路径
		const uploadDir = uploadConfig.getUploadDir();
		const fullPath = uploadConfig.ensureDir(uploadDir);

		// 保存路径信息以便后续使用
		req.uploadPath = uploadDir;

		cb(null, fullPath);
	},
	filename: function (req, file, cb) {
		const fileName = uploadConfig.generateFileName(file.originalname);
		cb(null, fileName);
	}
});

// 创建 multer 实例
const upload = multer({
	storage: storage,
	limits: uploadConfig.limits,
	fileFilter: (req, file, cb) => {
		if (uploadConfig.allowedMimeTypes.includes(file.mimetype)) {
			cb(null, true);
		} else {
			cb(new Error(`不支持的文件类型: ${file.mimetype}`));
		}
	}
});

/**
 * @typedef {import('moleculer').ServiceSchema} ServiceSchema Moleculer's Service Schema
 * @typedef {import('moleculer').Context} Context Moleculer's Context
 * @typedef {import('http').IncomingMessage} IncomingRequest Incoming HTTP Request
 * @typedef {import('http').ServerResponse} ServerResponse HTTP Server Response
 * @typedef {import('moleculer-web').ApiSettingsSchema} ApiSettingsSchema API Setting Schema
 */

module.exports = {
	name: "api",
	mixins: [ApiGateway],

	/** @type {ApiSettingsSchema} More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html */
	settings: {
		// Exposed port
		port: process.env.PORT || 3000,

		// Exposed IP
		ip: "0.0.0.0",

		// 禁用内置CORS处理器，使用自定义中间件
		cors: false,

		// Global Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares
		use: [
			// 限流中间件
			async function (req, res, next) {
				const clientIP = req.headers['x-forwarded-for'] ||
					req.connection.remoteAddress ||
					req.socket.remoteAddress ||
					(req.connection.socket ? req.connection.socket.remoteAddress : null);

				const now = Date.now();
				const windowMs = parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 60 * 1000; // 1分钟窗口
				const defaultMaxRequests = parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100;
				const authMaxRequests = parseInt(process.env.RATE_LIMIT_AUTH_MAX_REQUESTS) || 5;
				const maxRequests = req.url.startsWith('/api/auth/') ? authMaxRequests : defaultMaxRequests;

				const key = `${clientIP}:${req.url.split('?')[0]}`;
				const requests = await this.broker.cacher.get(key) || [];

				// 清理过期请求
				const validRequests = requests.filter(time => now - time < windowMs);

				if (validRequests.length >= maxRequests) {
					res.writeHead(429, { 'Content-Type': 'application/json' });
					res.end(JSON.stringify({
						code: 429,
						data: null,
						message: '请求过于频繁，请稍后再试',
						errorCode: 'RATE_LIMIT_EXCEEDED',
						timestamp: new Date().toISOString()
					}));
					return;
				}

				// 记录当前请求
				validRequests.push(now);
				await this.broker.cacher.set(key, validRequests, windowMs); // 缓存有效期与窗口一致
				next();
			},
			// 访问日志中间件
			function (req, res, next) {
				const startTime = Date.now();
				const requestId = Math.random().toString(36).substr(2, 9);

				// 记录请求开始
				console.log(`[${new Date().toISOString()}] [${requestId}] ${req.method} ${req.url} - 开始处理`);

				// 记录请求信息
				req.requestId = requestId;
				req.startTime = startTime;

				// 监听响应结束
				const originalEnd = res.end;
				res.end = function (...args) {
					const duration = Date.now() - startTime;
					const statusCode = res.statusCode;

					// 记录请求完成
					console.log(`[${new Date().toISOString()}] [${requestId}] ${req.method} ${req.url} - ${statusCode} (${duration}ms)`);

					// 记录异常状态码
					if (statusCode >= 400) {
						console.warn(`[${new Date().toISOString()}] [${requestId}] 异常响应: ${statusCode} - ${req.method} ${req.url}`);
					}

					originalEnd.apply(this, args);
				};

				next();
			},
			// CORS和安全头中间件
			function (req, res, next) {
				const origin = req.headers.origin;

				// 简化的CORS头设置 - 开发环境允许所有源
				if (process.env.NODE_ENV === 'development') {
					if (origin) {
						res.setHeader('Access-Control-Allow-Origin', origin);
						res.setHeader('Access-Control-Allow-Credentials', 'true');
					} else {
						res.setHeader('Access-Control-Allow-Origin', '*');
					}
				} else {
					// 生产环境的白名单检查
					const allowedOrigins = process.env.CORS_ORIGINS ?
						process.env.CORS_ORIGINS.split(',').map(o => o.trim()) :
						['https://yourdomain.com'];

					if (origin && allowedOrigins.includes(origin)) {
						res.setHeader('Access-Control-Allow-Origin', origin);
						res.setHeader('Access-Control-Allow-Credentials', 'true');
					} else if (!origin) {
						res.setHeader('Access-Control-Allow-Origin', '*');
					} else {
						console.warn(`CORS: 拒绝来自 ${origin} 的请求`);
						res.setHeader('Access-Control-Allow-Origin', 'null');
					}
				}

				// 设置CORS方法和头
				res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD');
				res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Request-ID, X-API-Key, Cache-Control, Pragma');
				res.setHeader('Access-Control-Expose-Headers', 'X-Request-ID, X-Total-Count, X-Page-Count, Content-Range, Accept-Ranges');
				res.setHeader('Access-Control-Max-Age', '86400');

				// 设置安全头
				res.setHeader('X-Content-Type-Options', 'nosniff');
				res.setHeader('X-Frame-Options', 'DENY');
				res.setHeader('X-XSS-Protection', '1; mode=block');
				res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
				res.setHeader('X-Request-ID', req.requestId);
				res.setHeader('X-Powered-By', 'Gulu Blog API');

				// 处理预检请求
				if (req.method === 'OPTIONS') {
					res.writeHead(200, {
						'Content-Type': 'application/json',
						'Content-Length': '0'
					});
					res.end();
					return;
				}

				// 检查Content-Type（排除GET和DELETE请求）
				if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') {
					const contentType = req.headers['content-type'];
					if (contentType && !contentType.includes('application/json') && !contentType.includes('multipart/form-data') && !contentType.includes('application/x-www-form-urlencoded')) {
						res.writeHead(415, { 'Content-Type': 'application/json' });
						res.end(JSON.stringify({
							code: 415,
							data: null,
							message: '不支持的Content-Type，请使用 application/json 或 multipart/form-data',
							errorCode: 'UNSUPPORTED_MEDIA_TYPE',
							requestId: req.requestId,
							timestamp: new Date().toISOString()
						}));
						return;
					}
				}

				next();
			},
			// 自定义静态文件服务中间件
			function (req, res, next) {
				// 只处理以 /uploads 或 /images 开头的请求
				if (req.url.startsWith('/uploads/') || req.url.startsWith('/images/')) {
					const filePath = path.join(__dirname, '..', req.url);

					// 检查文件是否存在
					if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
						// 设置正确的 Content-Type
						const ext = path.extname(filePath).toLowerCase();
						const mimeTypes = {
							'.jpg': 'image/jpeg',
							'.jpeg': 'image/jpeg',
							'.png': 'image/png',
							'.gif': 'image/gif',
							'.svg': 'image/svg+xml',
							'.webp': 'image/webp'
						};

						if (mimeTypes[ext]) {
							res.setHeader('Content-Type', mimeTypes[ext]);
						}

						// 读取并发送文件
						const stream = fs.createReadStream(filePath);
						stream.pipe(res);
						return;
					}
				}
				next();
			}
		],

		routes: [
			// ========================================
			// 公开路由（无需认证）- 博客前台访问
			// ========================================
			{
				path: "/api/public",
				whitelist: [
					"blog.getPersonalInfo",
					"categories.getAdminCategories",
					"tags.getAdminTags",
					"blog.getArticles",
					"blog.getArticleDetail",
					"settings.getSystemSettings",
					// 热门标签
					"tags.getHotTags",
					// 仪表盘数据（测试用）
					"dashboard.getDashboardData",
					"dashboard.getTrendData",
					"dashboard.getCategoryDistribution",
					"dashboard.collectStatsManually"
				],
				authentication: false,
				authorization: false,
				aliases: {
					// 健康检查
					'GET /health': (req, res) => {
						res.writeHead(200, { 'Content-Type': 'application/json' });
						res.end(JSON.stringify({ status: 'ok', timestamp: new Date().toISOString() }));
					},
					// 博客公开接口路由别名
					'GET /record-info': 'settings.getSystemSettings',
					'GET /personal-info': 'blog.getPersonalInfo',
					'GET /categories': 'categories.getAdminCategories',
					'GET /tags': 'tags.getAdminTags',
					'GET /articles': 'blog.getArticles',
					'GET /articles/:id': 'blog.getArticleDetail',
					// 热门标签接口
					'GET /tags/hot': 'tags.getHotTags',
					// 仪表盘数据接口（测试用）
					'GET /dashboard/data': 'dashboard.getDashboardData',
					'GET /dashboard/trends': 'dashboard.getTrendData',
					'GET /dashboard/category-distribution': 'dashboard.getCategoryDistribution',
					'POST /dashboard/collect': 'dashboard.collectStatsManually'
				},
				bodyParsers: {
					json: {
						strict: false,
						limit: "1MB"
					},
					urlencoded: {
						extended: true,
						limit: "1MB"
					}
				},
				mappingPolicy: "restrict",
				mergeParams: true,
				logging: true
			},
			// 静态文件路由
			{
				path: "",

				aliases: {
					// 静态文件服务
					'GET /uploads/**': function (req, res) {
						const fs = require('fs');
						const path = require('path');
						// 从URL中提取文件路径
						const urlPath = req.$params[0] || '';
						const filePath = path.join(__dirname, '..', 'uploads', urlPath);

						// 检查文件是否存在
						if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
							// 设置正确的 Content-Type
							const ext = path.extname(filePath).toLowerCase();
							const mimeTypes = {
								'.jpg': 'image/jpeg',
								'.jpeg': 'image/jpeg',
								'.png': 'image/png',
								'.gif': 'image/gif',
								'.svg': 'image/svg+xml',
								'.webp': 'image/webp'
							};

							res.writeHead(200, {
								'Content-Type': mimeTypes[ext] || 'application/octet-stream',
								'Cache-Control': 'public, max-age=31536000'
							});

							// 发送文件
							const stream = fs.createReadStream(filePath);
							stream.pipe(res);
						} else {
							res.writeHead(404, { 'Content-Type': 'application/json' });
							res.end(JSON.stringify({ error: 'File not found', path: urlPath }));
						}
					}
				},

				// 禁用身份验证
				authentication: false,
				authorization: false,

				// 禁用body解析
				bodyParsers: {
					json: false,
					urlencoded: false
				}
			},
			// ========================================
			// 认证路由（登录/注册等无需token的认证相关接口）
			// ========================================
			{
				path: "/api/auth",
				whitelist: [
					"admin.login"
				],
				authentication: false,
				authorization: false,
				aliases: {
					'POST /login': 'admin.login'
				},
				bodyParsers: {
					json: {
						strict: false,
						limit: "1MB"
					},
					urlencoded: {
						extended: true,
						limit: "1MB"
					}
				},
				mappingPolicy: "restrict",
				mergeParams: true,
				logging: true
			},
			// ========================================
			// 管理员路由（需要认证的所有管理功能）
			// ========================================
			{
				path: "/api/admin",

				whitelist: [
					// 认证相关（已登录用户的认证管理）
					"admin.me",
					"admin.logout",
					"admin.refresh",
					"admin.status",
					// 文章管理
					"admin.getAdminArticles",
					"admin.getAdminArticleDetail",
					"admin.createArticle",
					"admin.updateArticle",
					"admin.deleteArticle",
					"admin.batchDeleteArticles",
					"admin.updateArticleStatus",
					// 分类管理
					"categories.getAdminCategories",
					"categories.addCategory",
					"categories.updateCategory",
					"categories.deleteCategory",
					"categories.batchDeleteCategories",
					"categories.checkCategoryUsage",
					// 标签管理
					"tags.getAdminTags",
					"tags.addTag",
					"tags.updateTag",
					"tags.deleteTag",
					"tags.batchDeleteTags",
					"tags.checkTagUsage",
					// 图片管理
					"images.uploadImage",
					"images.getImages",
					"images.getImageDetail",
					"images.updateImage",
					"images.deleteImage",
					"images.batchDeleteImages",
					"images.getFileUploadConfig",
					// 设置管理
					"admin.getPersonalInfo",
					"admin.updatePersonalInfo",
					"admin.getProfile",
					"admin.updateProfile",
					"admin.uploadAvatar",
					"admin.changePassword",
					"admin.getSystemSettings",
					"admin.updateSystemSettings",
					// 仪表盘
					"admin.getDashboardStats",
					"admin.getDashboardChart",
					"dashboard.getDashboardData",
					"dashboard.getTrendData",
					"dashboard.getCategoryDistribution",
					"dashboard.collectStatsManually"
				],

				// Route-level Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares
				use: [
					// File upload middleware for upload endpoints
					function (req, res, next) {
						if (req.url.includes('/upload') || req.url.includes('/avatar')) {
							return upload.single('file')(req, res, (err) => {
								if (err) {
									return res.status(400).json({
										code: 400,
										data: null,
										message: err.message || '文件上传失败'
									});
								}
								// 将文件信息添加到req.body中，确保能通过ctx.params访问
								if (req.file) {
									req.body = req.body || {};
									req.body.$file = req.file;
									// Also add to req.$ctx.meta for easier access
									if (req.$ctx) {
										req.$ctx.meta = req.$ctx.meta || {};
										req.$ctx.meta.file = req.file;
									}
								}
								next();
							});
						}
						next();
					}
				],

				// Enable/disable parameter merging method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Disable-merging
				mergeParams: true,

				// Enable authentication. Implement the logic into `authenticate` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authentication
				authentication: true,

				// Enable authorization. Implement the logic into `authorize` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authorization
				authorization: false,

				// The auto-alias feature allows you to declare your route alias directly in your services.
				// The gateway will dynamically build the full routes from service schema.
				autoAliases: false,

				aliases: {
					// 认证管理（已登录用户）
					'GET /auth/me': 'admin.me',
					'POST /auth/logout': 'admin.logout',
					'POST /auth/refresh': 'admin.refresh',
					'GET /auth/status': 'admin.status',

					// 文章管理接口
					'GET /articles': 'admin.getAdminArticles',
					'GET /articles/:id': 'admin.getAdminArticleDetail',
					'POST /articles': 'admin.createArticle',
					'PUT /articles/:id': 'admin.updateArticle',
					'DELETE /articles/:id': 'admin.deleteArticle',
					'DELETE /articles/batch': 'admin.batchDeleteArticles',
					'PATCH /articles/:id/status': 'admin.updateArticleStatus',

					// 仪表盘统计接口
					'GET /dashboard/stats': 'admin.getDashboardStats',
					'GET /dashboard/chart': 'admin.getDashboardChart',
					'GET /dashboard/data': 'dashboard.getDashboardData',
					'GET /dashboard/trends': 'dashboard.getTrendData',
					'GET /dashboard/category-distribution': 'dashboard.getCategoryDistribution',
					'POST /dashboard/collect': 'dashboard.collectStatsManually',

					// 分类管理接口
					'GET /categories': 'categories.getAdminCategories',
					'POST /categories': 'categories.addCategory',
					'PUT /categories/:id': 'categories.updateCategory',
					'DELETE /categories/:id': 'categories.deleteCategory',
					'DELETE /categories/batch': 'categories.batchDeleteCategories',

					// 标签管理接口
					'GET /tags': 'tags.getAdminTags',
					'POST /tags': 'tags.addTag',
					'PUT /tags/:id': 'tags.updateTag',
					'DELETE /tags/:id': 'tags.deleteTag',
					'DELETE /tags/batch': 'tags.batchDeleteTags',

					// 分类和标签用量检查接口
					'GET /categories/:id/usage': 'categories.checkCategoryUsage',
					'GET /tags/:id/usage': 'tags.checkTagUsage',

					// 图片管理接口
					'POST /images/upload': 'images.uploadImage',
					'GET /images': 'images.getImages',
					'GET /images/:id': 'images.getImageDetail',
					'PUT /images/:id': 'images.updateImage',
					'DELETE /images/:id': 'images.deleteImage',
					'DELETE /images/batch': 'images.batchDeleteImages',
					'GET /files/config': 'images.getFileUploadConfig',

					// 设置管理接口
					'GET /settings/personal': 'admin.getPersonalInfo',
					'PUT /settings/personal': 'admin.updatePersonalInfo',
					'GET /settings/profile': 'admin.getProfile',
					'PUT /settings/profile': 'admin.updateProfile',
					'POST /settings/avatar': 'admin.uploadAvatar',
					'PUT /settings/password': 'admin.changePassword',
					'GET /settings/system': 'admin.getSystemSettings',
					'PUT /settings/system': 'admin.updateSystemSettings'
				},

				/**
				 * Before call hook. You can check the request.
				 * @param {Context} ctx
				 * @param {Object} route
				 * @param {IncomingRequest} req
				 * @param {ServerResponse} res
				 * @param {Object} data
				 */
				onBeforeCall(ctx, route, req, res) {
					// Set request headers to context meta for accessing in services
					ctx.meta.userAgent = req.headers["user-agent"];
					ctx.meta.headers = req.headers;
					ctx.meta.$location = {
						protocol: req.headers['x-forwarded-proto'] || (req.connection.encrypted ? 'https' : 'http'),
						host: req.headers.host || 'localhost:3000'
					};
				},

				/**
				 * After call hook. You can modify the data.
				 * @param {Context} ctx
				 * @param {Object} route
				 * @param {IncomingRequest} req
				 * @param {ServerResponse} res
				 * @param {Object} data
				onAfterCall(ctx, route, req, res, data) {
					// Async function which return with Promise
					return doSomething(ctx, res, data);
				}, */

				// Calling options. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Calling-options
				callOptions: {},

				bodyParsers: {
					json: {
						strict: false,
						limit: "1MB"
					},
					urlencoded: {
						extended: true,
						limit: "1MB"
					}
				},

				// Mapping policy setting. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Mapping-policy
				mappingPolicy: "restrict", // Available values: "all", "restrict"

				// Enable/disable logging
				logging: true
			}
		],

		// Do not log client side errors (does not log an error response when the error.code is 400<=X<500)
		log4XXResponses: false,
		// Logging the request parameters. Set to any log level to enable it. E.g. "info"
		logRequestParams: null,
		// Logging the response data. Set to any log level to enable it. E.g. "info"
		logResponseData: null,

		// 全局错误处理
		onError(req, res, err) {
			const requestId = req.requestId || 'unknown';

			// 记录错误日志
			this.logger.error(`[${requestId}] API错误:`, {
				url: req.url,
				method: req.method,
				error: err.message,
				stack: err.stack
			});

			// 设置响应头
			res.setHeader('Content-Type', 'application/json');
			res.setHeader('X-Request-ID', requestId);

			// 根据错误类型返回不同的响应
			if (err.code === 401) {
				res.statusCode = 401;
				res.end(JSON.stringify({
					code: 401,
					data: err.data || null,
					message: err.message || '认证失败',
					errorCode: err.data?.code || 'AUTH_FAILED',
					requestId,
					timestamp: new Date().toISOString()
				}));
			} else if (err.code === 403) {
				res.statusCode = 403;
				res.end(JSON.stringify({
					code: 403,
					data: null,
					message: err.message || '权限不足',
					errorCode: 'FORBIDDEN',
					requestId,
					timestamp: new Date().toISOString()
				}));
			} else if (err.code === 404) {
				res.statusCode = 404;
				res.end(JSON.stringify({
					code: 404,
					data: null,
					message: err.message || '资源未找到',
					errorCode: 'NOT_FOUND',
					requestId,
					timestamp: new Date().toISOString()
				}));
			} else if (err.code === 422) {
				res.statusCode = 422;
				res.end(JSON.stringify({
					code: 422,
					data: err.data || null,
					message: err.message || '参数验证失败',
					errorCode: 'VALIDATION_ERROR',
					requestId,
					timestamp: new Date().toISOString()
				}));
			} else {
				// 服务器内部错误
				res.statusCode = 500;
				res.end(JSON.stringify({
					code: 500,
					data: process.env.NODE_ENV === 'development' ? err.stack : null,
					message: '服务器内部错误',
					errorCode: 'SERVER_ERROR',
					requestId,
					timestamp: new Date().toISOString()
				}));
			}
		},


		// Serve assets from "public" folder. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Serve-static-files
		assets: {
			folder: "public",

			// Options to `server-static` module
			options: {}
		}
	},

	methods: {

		/**
		 * Authenticate the request. It check the `Authorization` token value in the request header.
		 * Check the token value & resolve the user by the token.
		 * The resolved user will be available in `ctx.meta.user`
		 *
		 * PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION!
		 *
		 * @param {Context} ctx
		 * @param {Object} route
		 * @param {IncomingRequest} req
		 * @returns {Promise}
		 */
		async authenticate(ctx, route, req) {
			// Skip authentication for public routes
			if (req.originalUrl && (
				req.originalUrl.startsWith('/api/public/') ||
				req.originalUrl.startsWith('/api/auth/') ||
				req.originalUrl.startsWith('/uploads/')
			)) {
				return null;
			}
			// Read the token from header
			const auth = req.headers["authorization"];

			if (!auth || !auth.startsWith("Bearer ")) {
				throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_NO_TOKEN, {
					error: "未提供认证令牌",
					code: "MISSING_TOKEN"
				});
			}

			const token = auth.slice(7);
			const isHave = await this.broker.cacher.get(`token:${token}`);
			if (!isHave) {
				ctx.meta.$statusCode = 401;
				return {
					code: 401,
					data: null,
					message: "令牌已失效，请重新登录"
				};
			}
			// 检查token格式
			if (!token || token.length < 10) {
				throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, {
					error: "无效的令牌格式",
					code: "INVALID_TOKEN_FORMAT"
				});
			}

			const jwtSecret = process.env.JWT_SECRET || "gulu-blog-admin-secret-key";

			try {
				// Verify JWT token
				const decoded = jwt.verify(token, jwtSecret);

				// 检查必需的token字段
				if (!decoded.id || !decoded.username || !decoded.role) {
					throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, {
						error: "令牌缺少必需字段",
						code: "INCOMPLETE_TOKEN"
					});
				}

				// 检查用户角色
				if (decoded.role !== 'admin') {
					throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, {
						error: "权限不足",
						code: "INSUFFICIENT_PERMISSIONS"
					});
				}

				// Set user info to context meta
				ctx.meta.user = {
					id: decoded.id,
					username: decoded.username,
					role: decoded.role,
					loginTime: decoded.iat,
					expiresAt: decoded.exp
				};
				ctx.meta.token = token;
				ctx.meta.requestTime = Date.now();

				return decoded;

			} catch (error) {
				// 如果是我们自己抛出的错误，直接重新抛出
				if (error instanceof ApiGateway.Errors.UnAuthorizedError) {
					throw error;
				}

				// JWT相关错误
				if (error.name === "TokenExpiredError") {
					throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, {
						error: "令牌已过期",
						code: "TOKEN_EXPIRED",
						expiredAt: error.expiredAt
					});
				} else if (error.name === "JsonWebTokenError") {
					throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, {
						error: "无效的令牌",
						code: "INVALID_TOKEN",
						details: error.message
					});
				} else if (error.name === "NotBeforeError") {
					throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, {
						error: "令牌尚未生效",
						code: "TOKEN_NOT_ACTIVE"
					});
				} else {
					// 记录未知错误
					this.logger.error("认证过程中发生未知错误:", error);
					throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, {
						error: "认证失败",
						code: "AUTH_FAILED"
					});
				}
			}
		},

		/**
		 * Authorize the request. Check that the authenticated user has right to access the resource.
		 *
		 * PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION!
		 *
		 * @param {Context} ctx
		 * @param {Object} route
		 * @param {IncomingRequest} req
		 * @returns {Promise}
		 */
		async authorize(ctx, route, req) {
			// Get the authenticated user.
			const user = ctx.meta.user;

			// It check the `auth` property in action schema.
			if (req.$action.auth == "required" && !user) {
				throw new ApiGateway.Errors.UnAuthorizedError("NO_RIGHTS");
			}
		}

	}
};
